
#include "EGraphics.h"
#include "EUtil.h"

namespace Eyas3D
{
	/* -------------------------------------------------------------------------- */
	EBitmap::EBitmap( const EString &filename ) : name( filename ), pixels( NULL ),
		pitch( 0 ), width( 0 ), height( 0 ), valid( false )
	{
#ifdef GRAPHICS_DGIPLAS
		bitmap = Gdiplus::Bitmap::FromFile( ToEWString( GetPath( filename ) ).c_str(), true );
		if ( bitmap )
		{
			bitmapData = new Gdiplus::BitmapData;
			Gdiplus::Rect rect( 0, 0, getWidth(), getHeight() );
			bitmap->LockBits( &rect, Gdiplus::ImageLockModeRead, PixelFormat32bppRGB, bitmapData );
			/* pixels[row * bitmapData->Stride / 4 + col] */
			pixels = (UINT *) bitmapData->Scan0;
			bitmap->UnlockBits( bitmapData );
			pitch = bitmapData->Stride / 4;
			width	= bitmap->GetWidth();
			height	= bitmap->GetHeight();
			valid = bitmap != NULL;
		}
#else
		/* 材质脚本, 首先获取默认路径 */
		hBitmap = (HBITMAP) ::LoadImage( GHInstance, GetPath( filename ).c_str(), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE );
		if ( hBitmap != NULL )
		{
			bitmapHDC = ::CreateCompatibleDC( NULL );
			::SelectObject( bitmapHDC, (HGDIOBJ) hBitmap );
			::GetObject( hBitmap, sizeof(BITMAP), &bitmap );
			width	= bitmap.bmWidth;
			height	= bitmap.bmHeight;
			pitch	= bitmap.bmHeight;
			valid	= true;
			pixels = new EColor[width * height];
			for ( EInt i = 0; i < getHeight(); i++ )
			{
				for ( EInt j = 0; j < getWidth(); j++ )
				{
					COLORREF color = ::GetPixel( bitmapHDC, i, j );
					pixels[j * width + i] = EColor( GetRValue( color ), GetGValue( color ), GetBValue( color ) );
				}
			}
		}
#endif
	}
	/* -------------------------------------------------------------------------- */
	EBitmap::~EBitmap()
	{
#ifdef GRAPHICS_DGIPLAS
		SafeDelete( bitmap );
#else
		DeleteObject( hBitmap );
		DeleteDC( bitmapHDC );
		SafeDeleteArray( pixels );
#endif
	}
	/* -------------------------------------------------------------------------- */
	EColor EBitmap::getPixel( EInt x, EInt y )
	{
#ifdef GRAPHICS_DGIPLAS
		/*
		 * Gdiplus::Color color;
		 * bitmap->GetPixel(x, y, &color);
		 * return EColor(color.GetR(), color.GetG(), color.GetB(), color.GetA());
		 */
		EInt color = pixels[x * pitch + y];
		return(EColor( color ) );
#else
		/*
		 * COLORREF color = ::GetPixel(bitmapHDC, x, y);
		 * return EColor(GetRValue(color), GetGValue(color), GetBValue(color));
		 * return pixels[y * getWidth() + x];
		 */
		return(pixels[y * pitch + x]);
#endif
	}
	/* -------------------------------------------------------------------------- */
	HINSTANCE GHInstance;
#ifdef  GRAPHICS_DGIPLAS
	Gdiplus::GdiplusStartupInput	EGraphics::GDiplusStartupInput;
	ULONG_PTR			EGraphics::GDiplusToken;
	Gdiplus::Bitmap		*EGraphics::GCurrentBuffer;
	Gdiplus::Graphics	*EGraphics::GCurrentGraphics;
	Gdiplus::Font *EGraphics::GFont;
	Gdiplus::BitmapData	*EGraphics::GBitmapData;
	UINT			*EGraphics::GPixels;
#else
	HBITMAP		EGraphics::GBufferedHandle;
	HDC		EGraphics::GBufferedHDC;
	HBRUSH		EGraphics::GBgBrush;
	HPEN		EGraphics::GPen;
	HINSTANCE	EGraphics::GInstance;
	RECT		EGraphics::GBufferSize;
	DIBSECTION	EGraphics::GDIBSection;
	BYTE		*EGraphics::GDatas;
	EInt		EGraphics::GPitch;
#endif
	EFloat *EGraphics::GZBuffer;
	/* -------------------------------------------------------------------------- */
	/* 初始化绘图系统 */
	bool EGraphics::initGraphics( HINSTANCE hinstance )
	{
#ifdef GRAPHICS_DGIPLAS
		Gdiplus::Status state = Gdiplus::GdiplusStartup( &GDiplusToken, &GDiplusStartupInput, NULL );
		if ( state != Gdiplus::Ok )
			return(false);
		{
			/* 主缓冲区和后缓冲区 */
			GCurrentBuffer = new Gdiplus::Bitmap( SCREEN_WIDTH, SCREEN_HEIGHT, PixelFormat32bppRGB );
			GCurrentGraphics = new Gdiplus::Graphics( GCurrentBuffer );
			GFont = new Gdiplus::Font( L"Arial", 10 );
			GBitmapData = new Gdiplus::BitmapData;
			Gdiplus::Rect rect( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
			GCurrentBuffer->LockBits( &rect, Gdiplus::ImageLockModeRead, PixelFormat32bppRGB, GBitmapData );
			/* pixels[row * bitmapData->Stride / 4 + col] */
			GPixels = (UINT *) GBitmapData->Scan0;
			GCurrentBuffer->UnlockBits( GBitmapData );
			/* 设置采样高质量 */
			GCurrentGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
		}
#else
		GBufferedHDC = ::CreateCompatibleDC( NULL );
		/* ////////////////// */
		BITMAPINFO info = { 0 };
		info.bmiHeader.biSize		= sizeof(info.bmiHeader);
		info.bmiHeader.biWidth		= SCREEN_WIDTH;
		info.bmiHeader.biHeight		= -SCREEN_HEIGHT;
		info.bmiHeader.biPlanes		= 1;
		info.bmiHeader.biBitCount	= 32;
		info.bmiHeader.biCompression	= BI_RGB;
		info.bmiHeader.biSizeImage	= SCREEN_WIDTH * SCREEN_HEIGHT * 32 / 8;
		/* 创建一块内存纹理并获取其数据指针 */
		void* pBits = NULL;
		GBufferedHandle = ::CreateDIBSection( GBufferedHDC, &info, DIB_RGB_COLORS, &pBits, NULL, 0 );
		::SelectObject( GBufferedHDC, GBufferedHandle );
		/* here: "dib.dsBm.bmBits" will points to the pixels of hdib. */
		::GetObject( GBufferedHandle, sizeof(DIBSECTION), &GDIBSection );
		GDatas	= (BYTE *) GDIBSection.dsBm.bmBits;
		GPitch	= GDIBSection.dsBm.bmWidthBytes;
		/* 设置刷新区域大小 */
		::SetRect( &GBufferSize, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
		/* GPen = (HPEN)::GetStockObject(WHITE_PEN); */
		GPen = ::CreatePen( PS_SOLID, 1, RGB( 255, 255, 255 ) );
		::SelectObject( GBufferedHDC, GPen );
		GBgBrush = ::CreateSolidBrush( RGB( 0, 0, 0 ) );
		::SelectObject( GBufferedHDC, GBgBrush );
		/* 设置字体 */
		HFONT hfnt = (HFONT) ::GetStockObject( OEM_FIXED_FONT );
		::SelectObject( GBufferedHDC, hfnt );
		/* 设置文字背景为透明色 */
		::SetBkMode( GBufferedHDC, TRANSPARENT );
#endif
		GZBuffer = new EFloat[SCREEN_WIDTH * SCREEN_HEIGHT];
		memset( GZBuffer, 0, sizeof(EFloat) * SCREEN_WIDTH * SCREEN_HEIGHT );
		return(true);
	}
	/* //-------------------------------------------------------------------------- */
	/* 关闭绘图系统 */
	void EGraphics::shutdownGraphics()
	{
#ifdef GRAPHICS_DGIPLAS
		{
			GCurrentBuffer		= 0;
			GCurrentGraphics	= 0;
			SafeDelete( GFont );
			SafeDelete( GCurrentGraphics );
			SafeDelete( GCurrentBuffer );
		}
		Gdiplus::GdiplusShutdown( GDiplusToken );
#else
		::DeleteObject( GPen );
		::DeleteObject( GBgBrush );
		::DeleteObject( GBufferedHandle );
		::DeleteDC( GBufferedHDC );
#endif
	}
	/* //-------------------------------------------------------------------------- */
	/* 清空当前缓冲区, 并将其颜色设置为黑色 */
	void EGraphics::clearBuffer( const EColor &c )
	{
#ifdef GRAPHICS_DGIPLAS
		if ( GCurrentGraphics )
			GCurrentGraphics->Clear( Gdiplus::Color( c.a, c.r, c.g, c.b ) );
#else
		/*
		 * HBRUSH bgBrush;
		 * bgBrush = ::CreateSolidBrush(RGB(c.r, c.g, c.b));
		 * ::SelectObject (GBufferedHDC, bgBrush) ;
		 */
		::FillRect( GBufferedHDC, &GBufferSize, GBgBrush );
		/*
		 * 重置深度缓存
		 * 注 : 这里memset只能对Int类型的数组进行初始化, 所以这里直接使用了Int类型
		 * 而没有使用float类型, 应该使用float
		 */
		::memset( GZBuffer, 0, sizeof(EFloat) * SCREEN_WIDTH * SCREEN_HEIGHT );
		/* ::DeleteObject(bgBrush); */
#endif
	}
	/* //-------------------------------------------------------------------------- */
	/* 在当前缓冲区内绘制一条线 */
	void EGraphics::drawLine( EInt x0, EInt y0, EInt x1, EInt y1, const EColor &c )
	{
#ifdef GRAPHICS_DGIPLAS
		if ( GCurrentGraphics )
		{
			Gdiplus::Pen pen( Gdiplus::Color( c.a, c.r, c.g, c.b ) );
			GCurrentGraphics->DrawLine( &pen, x0, y0, x1, y1 );
		}
#else
		/*
		 * HPEN hPen = ::CreatePen(PS_SOLID, 1, RGB(c.r, c.g, c.b));
		 * ::SelectObject(GBufferedHDC, hPen);
		 */
		::SelectObject( GBufferedHDC, GetStockObject( DC_PEN ) );
		::SetDCPenColor( GBufferedHDC, RGB( c.r, c.g, c.b ) );
		::MoveToEx( GBufferedHDC, x0, y0, NULL );
		::LineTo( GBufferedHDC, x1, y1 );
		/* ::DeleteObject(hPen); */
#endif
	}
	void EGraphics::drawString( const EString &str, EInt x, EInt y, const EColor &c )
	{
#ifdef GRAPHICS_DGIPLAS
		if ( GCurrentGraphics )
		{
			Gdiplus::PointF origin( x, y );
			Gdiplus::SolidBrush blackBrush( Gdiplus::Color( 255, c.r, c.g, c.b ) );
			GCurrentGraphics->DrawString( ToEWString( str ).c_str(), -1, GFont, origin, &blackBrush );
		}
#else
		::SetTextColor( GBufferedHDC, RGB( c.r, c.g, c.b ) );
		::TextOut( GBufferedHDC, x, y, str.c_str(), str.length() );
#endif
	}
	void EGraphics::fillTriangle( EInt x0, EInt y0, EInt x1, EInt y1, EInt x2, EInt y2, const EColor &c )
	{
#ifdef GRAPHICS_DGIPLAS
		if ( GCurrentGraphics )
		{
			Gdiplus::Point points[3];
			points[0].X	= x0, points[0].Y = y0;
			points[1].X	= x1, points[1].Y = y1;
			points[2].X	= x2, points[2].Y = y2;
			Gdiplus::SolidBrush blackBrush( Gdiplus::Color( 255, c.r, c.g, c.b ) );
			GCurrentGraphics->FillPolygon( &blackBrush, points, 3 );
		}
#else
#endif
	}
	void EGraphics::enableSmoothingMode( EBool enable )
	{
#ifdef GRAPHICS_DGIPLAS
		if ( enable )
		{
			GCurrentGraphics->SetSmoothingMode( Gdiplus::SmoothingModeHighQuality );
		}else  {
			GCurrentGraphics->SetSmoothingMode( Gdiplus::SmoothingModeDefault );
		}
#else
#endif
	}
	/* //-------------------------------------------------------------------------- */
	EBool EGraphics::checkZ( EInt x, EInt y, EFloat z )
	{
		/*
		 * 这里Z应该使用float类型来存储, 如果使用int类型, 那么会导致精度丢失
		 * 产生错误的现象
		 */
		EInt	index	= y * SCREEN_WIDTH + x;
		EFloat	divZ	= 1.0f / z;
		/* 这里是基于1/z做的比较 */
		if ( GZBuffer[index] > divZ )
			return(false);

		GZBuffer[index] = divZ;
		return(true);
	}
	/* //-------------------------------------------------------------------------- */
	void EGraphics::setPixel( EInt x, EInt y, /*EFloat z, */ const EColor &c )
	{
		/*
		 * 这里本来应该计算z值,但是为了避免对Image像素的读取, 我将z检查分离了出来
		 * 所以, 在调用setPixel之前应该先检测checkZ, 返回true在调用setPixel
		 * ----------------------------------------------------------------
		 * 这里Z应该使用float类型来存储, 如果使用int类型, 那么会导致精度丢失
		 * 产生错误的现象
		 * EInt index = y * SCREEN_WIDTH + x;
		 * EFloat divZ = 1.0f / z;
		 * // 这里是基于1/z做的比较
		 * if (GZBuffer[index] > divZ)
		 * return;
		 */
		/* GZBuffer[index] = divZ; */
#ifdef GRAPHICS_DGIPLAS
		if ( GCurrentBuffer )
		{
			/* GCurrentBuffer->SetPixel(x, y, Gdiplus::Color(c.a, c.r, c.g, c.b)); */
			GPixels[y * GBitmapData->Stride / 4 + x] = c.ToInt();
		}
#else
		/* SetPixel(GBufferedHDC, x, y, RGB(c.r, c.g, c.b)); */
		/*
		 * each scan line in a bitmap has a width, a.k.a : pitch
		 * int pitch = GDIBSection.dsBm.bmWidthBytes;
		 */
		BYTE* pSrcPix = GDatas + (GPitch * y);
		pSrcPix += x * 4;
		/* blue green red alpha */
		pSrcPix[0]	= c.b;
		pSrcPix[1]	= c.g;
		pSrcPix[2]	= c.r;
		pSrcPix[3]	= c.a;
#endif
	}
	/* //-------------------------------------------------------------------------- */
	EColor EGraphics::getPixel( EInt x, EInt y )
	{
#ifdef GRAPHICS_DGIPLAS
		if ( GCurrentBuffer )
		{
			/*
			 * Gdiplus::Color c;
			 * GCurrentBuffer->GetPixel(x, y, &c);
			 * return EColor(c.GetR(), c.GetG(), c.GetB(), c.GetA());
			 */
			EInt color = GPixels[x * GBitmapData->Stride / 4 + y];
			return(EColor( color ) );
		}
		return(EColor() );
#else
		/*
		 * COLORREF color = ::GetPixel(GBufferedHDC, x, y);
		 * return EColor(GetRValue(color), GetGValue(color), GetBValue(color));
		 */

		/*
		 * int pitch = GDIBSection.dsBm.bmWidthBytes;
		 * pointer that will advance in memory one pixel at a time...
		 */
		BYTE* pSrcPix = GDatas + (GPitch * y);
		pSrcPix += x * 4;
		return(EColor( pSrcPix[2], pSrcPix[1], pSrcPix[0] ) );
#endif
	}
	/* //-------------------------------------------------------------------------- */
	/* 将已经绘制好的缓冲区递交给Graphics在屏幕上绘制, 并将当前缓冲区设置为另一个缓冲区 */
	void EGraphics::fillBuffer( HDC hdc )
	{
#ifdef GRAPHICS_DGIPLAS
		if ( GCurrentGraphics )
		{
			Gdiplus::Graphics graphics( hdc );
			graphics.DrawImage( GCurrentBuffer, 0, 0 /*, SCREEN_WIDTH, SCREEN_HEIGHT*/ );
		}
#else
		::BitBlt( hdc, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, GBufferedHDC, 0, 0, SRCCOPY );
#endif
	}
	/* -------------------------------------------------------------------------- */
}
